package com.lifeng.config;

import com.lifeng.security.CustomAccessDeniedHandler;
import com.lifeng.security.CustomAuthenticationFailureHandler;
import com.lifeng.security.CustomUserDetailsService;
import com.lifeng.security.MyLogoutSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy;

import javax.annotation.Resource;


/**
 * Created by lifeng on 2019/8/9.
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) //开启方法权限控制
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Resource
    private CustomUserDetailsService customUserDetailsService;
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws  Exception{
        auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.formLogin()                    //  定义当需要用户登录时候，转到的登录页面。
                .loginPage("/tologin")           // 设置登录页面
                //.failureHandler(customAuthenticationFailureHandler())  //身份认证失败自定义失败处理,否则默认失败后转到到登录页面
                .and()
                .authorizeRequests()        //授权
                .antMatchers(
                        "/tologin",
                        "/login",
                        "/druid/**").permitAll()     // 设置所有人都可以访问登录页面
                .anyRequest()               // 任何请求,登录后可以访问
                .authenticated() //认证即登录
                .and()
                .csrf().disable();          // 关闭csrf防护

        //单用户登录，如果有一个登录了，同一个用户在其他地方登录将前一个剔除下线
        //http.sessionManagement().maximumSessions(1).expiredSessionStrategy(new MyExpiredSessionStrategy());

        //单用户登录，如果有一个登录了，同一个用户在其他地方不能登录
        http.sessionManagement()
                //.invalidSessionUrl("tologin") //配置当 session 无效的时候的跳转地址
                /*默认为 SimpleRedirectInvalidSessionStrategy ，可以参考这个类来写我们的实现*/
                //.invalidSessionStrategy(invalidSessionStrategy)//session无效处理策略,和上面session无效后跳转地址只能配置一个
                //控制最大登录数
                .maximumSessions(1)
                //当达到maximumSessions时，true表示不能踢掉前面的登录，false表示踢掉前面的用户
                .maxSessionsPreventsLogin(true)
                //当达到maximumSessions时，踢掉前面登录用户后的操作,这是默认实现(也可以自己实现,参考MyExpiredSessionStrategy)
                //.expiredSessionStrategy(new SimpleRedirectSessionInformationExpiredStrategy("/tologin"))
                .expiredUrl("/tologin"); //session过期时跳转的页面

        http.logout()   //退出登录相关配置
            //.logoutUrl("/exit")   //自定义退出登录页面 如果有此行配置 自己写的退出方法不会执行,
                //如果不配置此行 默认的退出 /logout 可以退出,同时会执行logoutSuccessHandler()
                //同时/logout 依然可以退出登录,
            .logoutSuccessHandler(new MyLogoutSuccessHandler()) //退出成功后要做的操作（如记录日志），和logoutSuccessUrl互斥
            //.logoutSuccessUrl("/index") //退出成功后跳转的页面
            .invalidateHttpSession(true)  //退出时销毁session
            .deleteCookies("JSESSIONID");    //退出时要删除的Cookies的名字
        //当用户没有授权时
        http.exceptionHandling()
                //.accessDeniedHandler(new CustomAccessDeniedHandler());  //权限不足自定义处理
                .accessDeniedPage("/unauthorization"); //accessDeniedHandler 和accessDeniedPage只能出现一个 权限不足时跳转的页面
        //解决不允许显示在iframe的问题
        http.headers().frameOptions().disable();

        //过滤器,可以在某个过滤器前或后加自定义过滤器，https://blog.csdn.net/baidu_38116275/article/details/80052408
        //在 UsernamePasswordAuthenticationFilter 前添加 BeforeLoginFilter
        //http.addFilterBefore(new BeforeLoginFilter(), UsernamePasswordAuthenticationFilter.class);
        //在 CsrfFilter 后添加 AfterCsrfFilter
        //http.addFilterAfter(new AfterCsrfFilter(), CsrfFilter.class);
    }



    @Bean
    public Md5PasswordEncoder passwordEncoder() {
        return new Md5PasswordEncoder();
    }


    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public AuthenticationFailureHandler customAuthenticationFailureHandler() {
        return new CustomAuthenticationFailureHandler();
    }

    /**
     * 解决不能注入session注册表问题
     */
    @Bean
    SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }
}
